#ifndef __BASE_FILE_LOG_H_
#define __BASE_FILE_LOG_H_

#include <sstream>
#include <string>
#include <fstream>
#include <signal.h>
#include <map>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "Mutex.h"
#include "Thread.h"
#include "Time.h"
#include <iostream>

#define LOG_BUFFER_SIZE 2048

namespace BASE{
	static const char * levelStr[] = {
		"ERROR",
		"INFO",
		"WARN",
		"DEBUG",
	};

	enum LOG_LEVEL{
		LEVEL_DEBUG = 0,
		LEVEL_INFO,
		LEVEL_WARN,
		LEVEL_ERROR,
	};

	typedef struct SNodeBuf{
		LOG_LEVEL level;
		std::ostringstream ss;
		std::string pipeBuf;
		std::string fileBuf;
		SNodeBuf():level(LEVEL_WARN){

		}
	}SNodeBuf;


	class FileLog: public Thread{
		public:

			FileLog();

			virtual ~FileLog();

			void stopFile();

			bool initialize(const std::string & _fileName, const std::string & _pipeName = "", LOG_LEVEL _level = LEVEL_WARN);

			const char * getLevel(LOG_LEVEL level){
				return levelStr[level];	
			}

			template<typename T>
			FileLog & operator << (const T & t){
				SNodeBuf * nodeBuf = getNode();
				if (nodeBuf->level < mLevel){
					std::cout << nodeBuf->level << "," << mLevel << std::endl;
					return *this;
				}
				nodeBuf->ss << t;
				//std::cout << nodeBuf->ss.str(); 
				nodeBuf->fileBuf += nodeBuf->ss.str();
				std::string::size_type pos = nodeBuf->fileBuf.find_last_of("\n");
				if (pos != std::string::npos){
					appendBuffer(nodeBuf->fileBuf);
					nodeBuf->fileBuf.clear();
				}else if (nodeBuf->fileBuf.length() > LOG_BUFFER_SIZE){
					(*this) << std::endl;
				}
				nodeBuf->pipeBuf += nodeBuf->ss.str();
				if (nodeBuf->pipeBuf.size() > LOG_BUFFER_SIZE){
					flushPipe(nodeBuf);
				}
				nodeBuf->ss.str("");
				return *this;
			}

			FileLog& operator << (LOG_LEVEL level){
				SNodeBuf * nodeBuf = getNode();
				nodeBuf->level = level;
				return *this;
			}

			/*FileLog& operator << (std::ios_base& (*pf)(std::ios_base&)){
				SNodeBuf * nodeBuf = getNode();
				if (nodeBuf->level < mLevel){
					return *this;
				}
				nodeBuf->ss << pf;
				return *this;
			}*/

			FileLog& operator << (std::ostream & (*pf)(std::ostream &)){
				//std::cout << "map size " << threadBuf.size() << std::endl;
				SNodeBuf * nodeBuf = getNode();
				if (nodeBuf->level < mLevel){
					return *this;
				}
				nodeBuf->ss << pf;
				std::string content = nodeBuf->ss.str();
				nodeBuf->fileBuf += content;
				nodeBuf->pipeBuf += content;
				if (pf == (std::ostream & (*)(std::ostream &))std::endl){
					appendBuffer(nodeBuf->fileBuf);
					nodeBuf->fileBuf.clear();
				}
				if (pf == (std::ostream &(*)(std::ostream &))std::flush){
					appendBuffer(nodeBuf->fileBuf);
					nodeBuf->fileBuf.clear();
					flushFile();
					flushPipe(nodeBuf);
				}
				nodeBuf->ss.str("");
				return *this;
			}

			void thread();

		private:
			SNodeBuf * getNode();
			
			void flushFile();

			void flushPipe(SNodeBuf * nodeBuf);

			void appendBuffer(const std::string & buf);

			void backupFile();

			bool openPipe();
		protected:
			LOG_LEVEL mLevel;
			std::string fileName;
			std::string pipeName;
			int pipeFd;
			bool pipeFlag;
			std::ofstream ofs;
			std::string buffer;
			Mutex bufMutex;
			std::map<pthread_t, SNodeBuf*> threadBuf;
			time_t backupTime;

	};
}

#endif
